/*
 * Copyright (c) 2001 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * @file libavcodec encoding video API usage example
 * @example encode_video.c
 *
 * Generate synthetic video data and encode it to an output file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C"{
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
}

void enCode(AVCodecContext *enCodecCtx, AVFrame *enFrame, AVPacket *enPkt, FILE *outFile){
    int ret;

//    if (enFrame){
//        printf("Send frame %3ld\n", enFrame->pts);
//    }

    ret = avcodec_send_frame(enCodecCtx, enFrame);
    if (ret < 0) {
        fprintf(stderr, "Error sending a frame for encoding\n");
        exit(1);
    }

    while (ret >= 0) {
        ret = avcodec_receive_packet(enCodecCtx, enPkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during encoding\n");
            exit(1);
        }

        printf("encode packet %3ld (size=%5d)\n", enPkt->pts, enPkt->size);
        fwrite(enPkt->data, 1, enPkt->size, outFile);
        av_packet_unref(enPkt);
    }
}

int main(int argc, char **argv)
{
    const char *rtspStream = "rtsp://admin:123456@192.168.12.150:554";
    const char *outFilePath = "output3.ts";

    //打开输出文件
    FILE *outFile;
    outFile = fopen(outFilePath, "wb");
    if (!outFile) {
        fprintf(stderr, "Could not open %s\n", outFilePath);
        exit(1);
    }

    //打开视频
    AVFormatContext *fmtCtx = avformat_alloc_context();
    AVInputFormat *ifmt = NULL;
    AVDictionary *dictCfg = NULL;
//    av_dict_set(&dictCfg, "buffer_size", "2048000", 0);
    av_dict_set(&dictCfg, "rtsp_transport", "udp", 0);
    av_dict_set(&dictCfg, "stimeout", "3000000", 0);
    av_dict_set(&dictCfg, "max_delay", "1000000", 0);
    av_dict_set(&dictCfg, "threads", "auto", 0);
    int ret = avformat_open_input(&fmtCtx, rtspStream, ifmt, &dictCfg);
    if (ret < 0) {
        fprintf(stderr, "could not open input\n");
        exit(1);
    }

    //查找流信息
    ret = avformat_find_stream_info(fmtCtx, NULL);
    if (ret < 0) {
        fprintf(stderr, "stream_info not found\n");
        exit(1);
    }

    //查找视频流
    int videoIndex = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (ret < 0) {
        fprintf(stderr, "best_stream not found\n");
        exit(1);
    }

    //获取解码器
    AVStream *videoStream = fmtCtx->streams[videoIndex];
    AVCodecID deCodecID = videoStream->codecpar->codec_id;

    const AVCodec *deCodec = avcodec_find_decoder(deCodecID);
    if (!deCodec) {
        fprintf(stderr, "deCodec not found\n");
        exit(1);
    }

    //初始化解码器上下文
    AVCodecContext *deCodecCtx= NULL;
    deCodecCtx = avcodec_alloc_context3(deCodec);
    if (!deCodecCtx) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    //复制源视频流的参数到解码器上下文
    ret = avcodec_parameters_to_context(deCodecCtx, fmtCtx->streams[videoIndex]->codecpar);
    if (ret < 0){
        fprintf(stderr, "Could not parameters_to_context\n");
        exit(1);
    }

    //打开解码器
    ret = avcodec_open2(deCodecCtx, deCodec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open deCodec");
        exit(1);
    }

    //设置编码器
    const AVCodec *enCodec;
    enCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG2VIDEO);
    if (!enCodec) {
        fprintf(stderr, "enCodec not found\n");
        exit(1);
    }

    //设置编码器上下文
    AVCodecContext *enCodecCtx = NULL;
    enCodecCtx = avcodec_alloc_context3(enCodec);

    //设置自定义的编码器上下文参数
    enCodecCtx->bit_rate = 40000;
    enCodecCtx->width = deCodecCtx->width;
    enCodecCtx->height = deCodecCtx->height;
    int num = deCodecCtx->framerate.num;
    int den = deCodecCtx->framerate.den;
    enCodecCtx->time_base = (AVRational){den, num};
    enCodecCtx->framerate = (AVRational){num, den};
    enCodecCtx->gop_size = 10;
    enCodecCtx->max_b_frames = 1;
    enCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    enCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    av_opt_set(enCodecCtx->priv_data, "preset", "slow", 0);

    //打开编码器
    ret = avcodec_open2(enCodecCtx, enCodec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open enCodec");
        exit(1);
    }

    //创建编码需要的帧
    AVPacket *enPkt;
    enPkt = av_packet_alloc();

    //读取一帧
    AVPacket *dePkt;
    dePkt = av_packet_alloc();
    AVFrame *deFrame;
    deFrame = av_frame_alloc();
    if (!dePkt){
        fprintf(stderr, "Could not alloc pkt");
        exit(1);
    }
    int flag = 0;
    while(flag<500){
        ret = av_read_frame(fmtCtx, dePkt);
        if(ret >= 0){
            int index = dePkt->stream_index;
            if (index != videoIndex){
                av_packet_unref(dePkt);
                continue;
            }
            ret = avcodec_send_packet(deCodecCtx, dePkt);
            if (ret < 0) {
                fprintf(stderr, "Error sending a packet for decoding\n");
                av_packet_unref(dePkt);
                continue;
            }
            while (ret >= 0) {
                ret = avcodec_receive_frame(deCodecCtx, deFrame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                    av_packet_unref(dePkt);
                    break;
                }
                else if (ret < 0) {
                    fprintf(stderr, "Error during decoding\n");
                    av_packet_unref(dePkt);
                    break;
                }

                printf("decode frame %3ld\n", deCodecCtx->frame_num);
                fflush(stdout);

                //编码
                if (ret < 0){
                    exit(1);
                }
//                enFrame->pts = pts;
//                printf("%ld", (long)deFrame->pts);
                enCode(enCodecCtx, deFrame, enPkt, outFile);
            }
            ++flag;
        }
        av_packet_unref(dePkt);
    }

    fclose(outFile);

    avformat_close_input(&fmtCtx);
    av_dict_free(&dictCfg);
    avcodec_free_context(&deCodecCtx);
    avcodec_free_context(&enCodecCtx);
    av_frame_free(&deFrame);
    av_packet_free(&dePkt);
    av_packet_free(&enPkt);

    return 0;
}
